using System;
using System.Collections;
using System.Data;
using System.Text;
using System.IO;

using Mono.Cecil;
using Mono.Cecil.Cil;

using FieldStat.DataCollection;
using FieldStat.CodeModel;

namespace FieldStat.Plugins
{
    public class RSF_Plugin : AbstractPlugin
    {
        public RSF_Plugin() : base( true ){}

        ArrayList sequences;
        ArrayList klasses;
        public override void SetOutputTable(object result)
        {
            object[] tuple = (object[])((AbstractCollector)result).Values;
            sequences = (ArrayList)tuple[0];
            klasses   = (ArrayList)tuple[1];
        }

        public override AbstractCollector CreateCollector()
        {
            return new RSF_Collector();
        }

        public override void Export(Results results)
        {
            StreamWriter sw = new StreamWriter("RSF_Output.txt");
            foreach (string seq in sequences)
            {
                sw.WriteLine(seq);
            }
            sw.Close();

            StreamWriter sw2 = new StreamWriter("RSF_Classes.txt");
            foreach (string klass in klasses )
            {
                sw2.WriteLine(klass);
            }
            sw2.Close();
        }
    }

    public class RSF_Collector : AbstractCollector
    {
        ArrayList sequences = new ArrayList();
        ArrayList klasses = new ArrayList();

        public override ICollection Values
        {
            get
            {
                return new object[] { sequences, klasses };
            }
        }

        public override void OnMethodBody(MethodBody body)
        {
            string klass = CodeProperties.GetClassName( body.Method.DeclaringType );
            string meth  = CodeProperties.GetFullNameTight( body.Method, true );
            sequences.Add( "DEFINE " + klass + " "  + meth);
        }

        public override void  OnType(TypeDefinition type)
        {
            string klass = CodeProperties.GetClassName(type);
 	        foreach( FieldDefinition field in type.Fields )
            {
                sequences.Add("DEFINE " + klass + " " + field.Name);
            }
            foreach (PropertyDefinition prop in type.Properties)
            {
                sequences.Add("DEFINE " + klass + " " + prop.Name );
            }
            if (type.IsAbstract)
            {
                klasses.Add("ABSTRACT " + klass);
            }
            else
            {
                klasses.Add("CONCRETE " + klass);
            }
            if (type.BaseType != null)
            {
                string parent = CodeProperties.GetClassName(type.BaseType);
                klasses.Add("CHILDOF " + klass + " " + parent);
            }
            //if( type.BaseType
            // ABSTRACT class
            // CONCRETE class
            // CHILDOF  subclass superclass
        }

        public override void  OnMethodCall(MethodDefinition callingContext, MethodReference methodCall, string callTypeName, 
            string callMethodName, bool isSystem, bool isExternal, Instruction call)
        {
            if (isSystem)
                return;
            if (call.OpCode == OpCodes.Callvirt)
            {
                string md = CodeProperties.GetFullNameTight(callingContext,true);
                string klass = CodeProperties.GetClassName(methodCall.DeclaringType);
                string mc = CodeProperties.GetFullNameTight(methodCall,true);
                sequences.Add("CALLVI " + md + " " + klass + " " + mc);
            }
            else
            {
                string md = CodeProperties.GetFullNameTight(callingContext,true);
                string mc = CodeProperties.GetFullNameTight(methodCall,true);
                sequences.Add("CALLST " + md + " " + mc);
            }
        }

        public override void OnFieldReference(MethodDefinition callingContext, FieldReference field, Instruction i)
        {
            string md = CodeProperties.GetFullNameTight(callingContext,true);
            string fld = CodeProperties.GetClassName(field.DeclaringType) + field.Name;
            sequences.Add("CALLST " + md + " " +  fld);
        }
    }
}
